package org.abacus.dbtojava;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

public class JavaFileService1Dot4 extends FileService {

	@Override
	String getPath(FileType fileType) {
		String ms = fileType.name().toLowerCase();
		StringBuilder sb = new StringBuilder();
		sb.append(Config.getString(ms + ".src"));
		sb.append(File.separator);
		sb.append(this.getPackage(fileType).replace(".", File.separator));
		return sb.toString();
	}

	@Override
	String getPackage(FileType fileType) {
		String ms = fileType.name().toLowerCase();
		return getValue(ms + ".package");
	}

	private String getValue(String key) {
		Object value = Config.getString(key);
		if (null == value) {
			return "";
		}
		return value.toString();
	}

	@Test
	public void getPath() throws IOException {
		this.createModelFile();
		this.createDaoFile();
		this.createDaoImplFile();
		this.createServiceFile();
		this.createServiceImplFile();
		this.createHbmXmlFile();
	}

	@Override
	String getFileName(FileType fileType, String table) {
		String ms = fileType.name().toLowerCase();
		String suffix = Config.getString(ms + ".suffix");
		if (null == suffix) {
			suffix = "";
		}
		if (fileType == FileType.HBMXML) {
			return getClassName(table) + suffix + ".hbm.xml";
		}
		return getClassName(table) + suffix + ".java";
	}

	@Override
	String getModeContent(String table, List<Column> columns) {
		StringBuilder sb = new StringBuilder();
		sb.append("package ");
		sb.append(this.getPackage(FileType.MODEL));
		sb.append(Config.SEMICOLON);
		sb.append(Config.NEW_LINE);
		sb.append(Config.NEW_LINE);
		sb.append("public class ");
		sb.append(this.getClassName(table));
		sb.append(this.getModelExtends(table));
		sb.append(this.getModelImplements(table));
		sb.append("{");
		sb.append(Config.NEW_LINE);
		sb.append(Config.TAB);
		sb.append(Config.NEW_LINE);

		if (modleSerializable(table)) {
			sb.append(Config.TAB);
			sb.append("private static final long serialVersionUID = " + RANDOM.nextLong() + "L");
			sb.append(Config.SEMICOLON);
			sb.append(Config.NEW_LINE);
			sb.append(Config.NEW_LINE);
		}

		List<String> modelAttributeFilter = this.getStringList(this.getValue("model.attribute.filter"));
		List<String> modelTableAttributeFilter = this.getStringList(this.getValue("model." + table.toLowerCase() + ".attribute.filter"));
		sb.append(this.getModeContent(columns, modelAttributeFilter, modelTableAttributeFilter));
		sb.append("}");
		return sb.toString();
	}

	private boolean modleSerializable(String table) {
		Object value = Config.getString("model." + table.toLowerCase() + ".serializable");
		if (null == value) {
			value = Config.getString("model.serializable");
		}
		if (null == value) {
			return false;
		}
		return Boolean.valueOf(value.toString());
	}

	private List<String> getStringList(String value) {
		return Arrays.asList(value.split(Config.COMMA));
	}

	private String getModelExtends(String table) {
		Object value = Config.getString("model." + table.toLowerCase() + ".extends");
		if (null == value) {
			value = Config.getString("model.extends");
		}
		if (null == value) {
			return "";
		}
		return " extends " + value.toString();
	}

	private String getModelImplements(String table) {
		Object value = Config.getString("model." + table.toLowerCase() + ".implements");
		if (null == value) {
			value = Config.getString("model.implements");
		}
		if (null == value) {
			return "";
		}
		return " implements " + value.toString();
	}

	private String getModeContent(List<Column> columns, List<String> modelAttributeFilter, List<String> modelTableAttributeFilter) {
		StringBuilder sb = new StringBuilder();
		StringBuilder getterAndSetter = new StringBuilder();
		String attributeName;
		String columnName;
		String columnType;
		String columnNameLowerCase;
		String className;
		for (Column column : columns) {
			columnName = column.column_name;
			columnType = TypeMapping.getJavaTypes(column.data_type);
			columnNameLowerCase = columnName.toLowerCase();
			// filter
			if (modelAttributeFilter.contains(columnNameLowerCase) || modelTableAttributeFilter.contains(columnNameLowerCase)) {
				continue;
			}
			attributeName = this.getAttributeName(columnName);
			className = this.getClassName(columnName);
			// attribute
			sb.append(Config.TAB);
			sb.append("private");
			sb.append(Config.SPACE);
			sb.append(columnType);
			sb.append(Config.SPACE);
			sb.append(attributeName);
			sb.append(Config.SEMICOLON);
			sb.append(Config.NEW_LINE);
			sb.append(Config.NEW_LINE);
			// getter
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append("public");
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append(columnType);
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append(Config.GETTER);
			getterAndSetter.append(className);
			getterAndSetter.append("() {");
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append(Config.RETURNS);
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append(attributeName);
			getterAndSetter.append(Config.SEMICOLON);
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append("}");
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.NEW_LINE);
			// setter
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append("public");
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append("void");
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append(Config.SETTER);
			getterAndSetter.append(className);
			getterAndSetter.append("(");
			getterAndSetter.append(columnType);
			getterAndSetter.append(Config.SPACE);
			getterAndSetter.append(attributeName);
			getterAndSetter.append(") {");
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append("this.");
			getterAndSetter.append(attributeName);
			getterAndSetter.append(" = ");
			getterAndSetter.append(attributeName);
			getterAndSetter.append(Config.SEMICOLON);
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.TAB);
			getterAndSetter.append("}");
			getterAndSetter.append(Config.NEW_LINE);
			getterAndSetter.append(Config.NEW_LINE);
		}
		sb.append(getterAndSetter);
		return sb.toString();
	}

	private static final Map<String, String> templateContents = new HashMap<String, String>();

	private String getDaoContent() {
		return this.getTemplate(Config.DAO_TEMPLATE);
	}

	private String getDaoImplContent() {
		return this.getTemplate(Config.DAO_IMPL_TEMPLATE);
	}

	private String getServiceContent() {
		return this.getTemplate(Config.SERVICE_TEMPLATE);
	}

	private String getServiceImplContent() {
		return this.getTemplate(Config.SERVICE_IMPL_TEMPLATE);
	}

	private String getTemplate(String templateName) {
		String template = templateContents.get(templateName);
		if (null != template) {
			return template;
		}
		InputStream is = JavaFileService1Dot4.class.getResourceAsStream("/" + templateName);
		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(isr);
		try {
			String line = br.readLine();
			StringBuilder sb = new StringBuilder();
			while (null != line) {
				sb.append(line);
				sb.append(Config.NEW_LINE);
				line = br.readLine();
			}
			template = sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != br) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != isr) {
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != is) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		if (null != template) {
			templateContents.put(templateName, template);
		}
		return template;
	}

	@Override
	String getDaoContent(String table) {
		String content = this.getDaoContent();
		String className = this.getClassName(table);
		String extendsName = Config.getString("dao.extends");
		if (null == extendsName) {
			content = content.replace(" {extends}", "");
		} else {
			content = content.replace("{extends}", "extends " + extendsName);
		}
		content = content.replace("{modelName}", className);
		content = content.replace("{suffix}", Config.getString("dao.suffix"));
		content = content.replace("{package}", this.getPackage(FileType.DAO));
		content = content.replace("{model.package}", this.getPackage(FileType.MODEL));
		content = content.replace("{serialVersionUID}", "private static final long serialVersionUID = " + RANDOM.nextLong() + "L");
		List<Column> columns = this.dbService.getColumns(table);
		StringBuffer mainContent = new StringBuffer();
		if (null != columns && columns.size() > 0) {
			Set<String> primaryKeys = this.getPrimaryKeys(table);
			String attri = "\n\tjava.util.List findBy{attributeName}({attributeType} {arg0});\n";
			String pk = "\n\t{modelName} findBy{attributeName}({attributeType} {arg0});\n";
			String t;
			for (Column column : columns) {
				if (equalsIgnoreCase(primaryKeys, column.column_name)) {
					t = pk;
				} else {
					t = attri;
				}
				String rt = t.replace("{modelName}", className);
				rt = rt.replace("{attributeName}", this.getClassName(column.column_name));
				String columnType = TypeMapping.getJavaTypes(column.data_type);
				rt = rt.replace("{attributeType}", columnType);
				rt = rt.replace("{arg0}", this.getAttributeName(column.column_name));
				mainContent.append(rt);
			}
		}
		content = content.replace("{content}", mainContent.toString());
		return content;
	}

	@Override
	String getDaoImplContent(String table) {
		String content = this.getDaoImplContent();
		String className = this.getClassName(table);
		String extendsName = Config.getString("daoimpl.extends");
		if (null == extendsName) {
			content = content.replace(" {extends}", "");
		} else {
			content = content.replace("{extends}", "extends " + extendsName);
		}

		String implementsName = Config.getString("daoimpl.implements");
		if (null == implementsName) {
			content = content.replace(" {implements}", "");
		} else {
			if ("dao".equalsIgnoreCase(implementsName)) {
				String impls = this.getDaoImplements(className);
				content = content.replace("{implements}", impls);
			} else {
				content = content.replace("{implements}", "implements " + implementsName);
			}
		}

		content = content.replace("{modelName}", className);
		content = content.replace("{suffix}", Config.getString("daoimpl.suffix"));
		content = content.replace("{package}", this.getPackage(FileType.DAOIMPL));
		content = content.replace("{model.package}", this.getPackage(FileType.MODEL));
		content = content.replace("{serialVersionUID}", "private static final long serialVersionUID = " + RANDOM.nextLong() + "L");

		List<Column> columns = this.dbService.getColumns(table);
		StringBuffer mainContent = new StringBuffer();
		if (null != columns && columns.size() > 0) {
			String attri = "\n\tpublic java.util.List findBy{attributeName}({attributeType} {arg0}) {\n\t\treturn {return};\n\t}\n";
			String pk = "\n\tpublic {modelName} findBy{attributeName}({attributeType} {arg0}) {\n\t\treturn {return};\n\t}\n";
			String attri_re = "this.getSession().createCriteria(getType()).add(org.hibernate.criterion.Restrictions.{relationalOperator}(\"{attributeName}\", {attributeName})).list()";
			String pk_re = "({modelName}) this.getSession().get(getType(), {attributeName})";
			Set<String> primaryKeys = this.getPrimaryKeys(table);
			String t;
			String re;
			String attributeName;
			for (Column column : columns) {
				String columnType = TypeMapping.getJavaTypes(column.data_type);
				if (equalsIgnoreCase(primaryKeys, column.column_name)) {
					t = pk;
					re = pk_re;
				} else {
					t = attri;
					re = attri_re;
					if ("java.lang.String".equalsIgnoreCase(columnType)) {
						re = re.replace("{relationalOperator}", "like");
					} else {
						re = re.replace("{relationalOperator}", "eq");
					}
				}
				String rt = t.replace("{modelName}", className);
				rt = rt.replace("{attributeName}", this.getClassName(column.column_name));
				rt = rt.replace("{attributeType}", columnType);
				attributeName = this.getAttributeName(column.column_name);
				rt = rt.replace("{arg0}", attributeName);
				re = re.replace("{modelName}", className);
				re = re.replace("{attributeName}", attributeName);
				rt = rt.replace("{return}", re);
				mainContent.append(rt);
			}
		}
		String getType = "\n\tprotected Class getType() {\n\t\treturn {modelName}.class;\n\t}\n";
		getType = getType.replace("{modelName}", className);
		mainContent.append(getType);
		content = content.replace("{content}", mainContent.toString());
		return content;
	}

	private String getDaoImplements(String className) {
		return "implements " + this.getPackage(FileType.DAO) + "." + className + Config.getString("dao.suffix");
	}

	@Override
	String getServiceContent(String table) {
		String content = this.getServiceContent();
		String className = this.getClassName(table);

		String extendsName = Config.getString("service.extends");
		if (null == extendsName) {
			content = content.replace(" {extends}", "");
		} else {
			content = content.replace("{extends}", "extends " + extendsName);
		}
		content = content.replace("{modelName}", className);
		content = content.replace("{suffix}", Config.getString("service.suffix"));
		content = content.replace("{package}", this.getPackage(FileType.SERVICE));
		content = content.replace("{model.package}", this.getPackage(FileType.MODEL));
		content = content.replace("{serialVersionUID}", "private static final long serialVersionUID = " + RANDOM.nextLong() + "L");
		List<Column> columns = this.dbService.getColumns(table);
		StringBuffer mainContent = new StringBuffer();
		if (null != columns && columns.size() > 0) {
			String attri = "\n\tjava.util.List getBy{attributeName}({attributeType} {arg0});\n";
			String pk = "\n\t{modelName} getBy{attributeName}({attributeType} {arg0});\n";
			Set<String> primaryKeys = this.getPrimaryKeys(table);
			String t;
			for (Column column : columns) {
				if (equalsIgnoreCase(primaryKeys, column.column_name)) {
					t = pk;
				} else {
					t = attri;
				}
				String rt = t.replace("{modelName}", className);
				rt = rt.replace("{attributeName}", this.getClassName(column.column_name));
				String columnType = TypeMapping.getJavaTypes(column.data_type);
				rt = rt.replace("{attributeType}", columnType);
				rt = rt.replace("{arg0}", this.getAttributeName(column.column_name));
				mainContent.append(rt);
			}
		}
		content = content.replace("{content}", mainContent.toString());
		return content;
	}

	@Override
	String getActionContent(String table) {
		return null;
	}

	@Override
	String getServiceImplContent(String table) {
		String content = this.getServiceImplContent();
		String className = this.getClassName(table);

		String extendsName = Config.getString("serviceimpl.extends");
		if (null == extendsName) {
			content = content.replace(" {extends}", "");
		} else {
			content = content.replace("{extends}", "extends " + extendsName);
		}

		String implementsName = Config.getString("serviceimpl.implements");
		if (null == implementsName) {
			content = content.replace(" {implements}", "");
		} else {
			if ("service".equalsIgnoreCase(implementsName)) {
				String impls = this.getServiceImplements(className);
				content = content.replace("{implements}", impls);
			} else {
				content = content.replace("{implements}", "implements " + implementsName);
			}
		}

		content = content.replace("{modelName}", className);
		content = content.replace("{suffix}", Config.getString("serviceimpl.suffix"));
		content = content.replace("{package}", this.getPackage(FileType.SERVICEIMPL));
		content = content.replace("{model.package}", this.getPackage(FileType.MODEL));
		content = content.replace("{serialVersionUID}", "private static final long serialVersionUID = " + RANDOM.nextLong() + "L");
		List<Column> columns = this.dbService.getColumns(table);

		FileType fileType = FileType.DAO;
		String daoPackage = this.getPackage(fileType);
		String daoSuffix = Config.getString("dao.suffix");
		String daocontext = "\n\tprivate {daoPackage}.{modelName}{daoSuffix} dao;\n\n\tpublic {daoPackage}.{modelName}{daoSuffix} getDao() {\n\t\treturn dao;\n\t}\n\n";
		daocontext += "\tpublic {daoPackage}.{modelName}{daoSuffix} setDao({daoPackage}.{modelName}Dao dao) {\n\t\treturn this.dao = dao;\n\t}\n";
		daocontext = daocontext.replace("{modelName}", className);
		daocontext = daocontext.replace("{daoPackage}", daoPackage);
		daocontext = daocontext.replace("{daoSuffix}", daoSuffix);
		StringBuffer mainContent = new StringBuffer(daocontext);
		if (null != columns && columns.size() > 0) {
			String attri = "\n\tpublic java.util.List getBy{attributeName}({attributeType} {arg0}) {\n\t\treturn {return};\n\t}\n";
			String pk = "\n\tpublic {modelName} getBy{attributeName}({attributeType} {arg0}) {\n\t\treturn {return};\n\t}\n";
			String attri_re = "this.getDao().findBy{attributeClassName}({attributeName})";
			String pk_re = "this.getDao().findBy{attributeClassName}({attributeName})";
			Set<String> primaryKeys = this.getPrimaryKeys(table);
			String t;
			String re;
			String attributeName;
			String attributeClassName;
			for (Column column : columns) {
				String columnType = TypeMapping.getJavaTypes(column.data_type);
				if (equalsIgnoreCase(primaryKeys, column.column_name)) {
					t = pk;
					re = pk_re;
				} else {
					t = attri;
					re = attri_re;
					if ("java.lang.String".equalsIgnoreCase(columnType)) {
						re = re.replace("{relationalOperator}", "like");
					} else {
						re = re.replace("{relationalOperator}", "eq");
					}
				}
				String rt = t.replace("{modelName}", className);
				attributeClassName = this.getClassName(column.column_name);
				rt = rt.replace("{attributeName}", attributeClassName);
				rt = rt.replace("{attributeType}", columnType);
				attributeName = this.getAttributeName(column.column_name);
				rt = rt.replace("{arg0}", attributeName);

				re = re.replace("{attributeClassName}", attributeClassName);
				re = re.replace("{attributeName}", attributeName);
				rt = rt.replace("{return}", re);
				mainContent.append(rt);
			}
		}
		content = content.replace("{content}", mainContent.toString());
		return content;
	}

	private String getServiceImplements(String className) {
		return "implements " + this.getPackage(FileType.SERVICE) + "." + className + Config.getString("service.suffix");
	}

	boolean equalsIgnoreCase(Collection<String> arg0, String arg1) {
		if (null == arg1) {
			return false;
		}
		if (null == arg0 || arg0.size() == 0) {
			return false;
		}
		Iterator<String> iterator = arg0.iterator();
		String next;
		while (iterator.hasNext()) {
			next = iterator.next();
			if (null != next) {
				if (next.equalsIgnoreCase(arg1)) {
					return true;
				}
			}
		}
		return false;
	}

	private Set<String> getPrimaryKeys(String table) {
		List<PrimaryKey> list = dbService.getPrimaryKeys(table);
		Set<String> primaryKeys = new HashSet<String>();
		if (null == list || list.size() == 0) {
			String id = Config.getString("hbmxml.id");
			primaryKeys.add(id);
		} else {
			for (PrimaryKey pk : list) {
				primaryKeys.add(pk.column_name);
			}
		}
		return primaryKeys;
	}

	@Override
	String getHbmXmlContent(String table, List<Column> columns) {
		String property = "\n\t\t<property name=\"{attributeName}\" type=\"{type}\">\n\t\t\t<column name=\"{columnName}\"></column>\n\t\t</property>";
		String content = this.getTemplate(Config.HBM_XML_TEMPLATE);
		String className = this.getClassName(table);
		content = content.replace("{className}", this.getPackage(FileType.MODEL) + "." + className);
		content = content.replace("{tableName}", table.toUpperCase());
		String attributeName;
		String columnName;
		String columnType;
		String temp;
		List<PrimaryKey> list = dbService.getPrimaryKeys(table);
		Set<String> primaryKeys = new HashSet<String>();
		if (null == list || list.size() == 0) {
			String id = Config.getString("hbmxml.id");
			primaryKeys.add(id);
		} else {
			for (PrimaryKey pk : list) {
				primaryKeys.add(pk.column_name);
			}
		}
		StringBuilder sb = new StringBuilder();
		String idContent = "";
		for (Column column : columns) {
			columnName = column.column_name;
			columnType = TypeMapping.getJavaTypes(column.data_type);
			attributeName = this.getAttributeName(columnName);
			// id
			if (equalsIgnoreCase(primaryKeys, columnName)) {
				if (primaryKeys.size() > 1) {
					idContent += "\n\t\t\t<key-property name=\"" + attributeName + "\" column=\"" + columnName.toUpperCase() + "\" type=\""
							+ columnType + "\"/>";
				} else {
					idContent = this.getTemplate(Config.ID_HBM_XML_TEMPLATE);
					idContent = idContent.replace("{id}", columnType);
					idContent = idContent.replace("{idName}", columnName.toUpperCase());
				}
				continue;
			}
			temp = property;
			// property
			temp = temp.replace("{attributeName}", attributeName);
			temp = temp.replace("{columnName}", columnName.toUpperCase());
			temp = temp.replace("{type}", columnType);
			sb.append(temp);
		}
		if (primaryKeys.size() > 1) {
			String compositeId = this.getTemplate(Config.COMPOSITE_ID_HBM_XML_TEMPLATE);
			idContent = compositeId.replace("{composite-id}", idContent);
		}
		if (idContent.endsWith("\n")) {
			idContent = idContent.substring(0, idContent.length() - 1);
		}
		content = content.replace("{id}", idContent);
		idContent = sb.toString();
		List<ExportedKey> eksList = dbService.getExportedKeys(table);
		if (null != eksList) {
			String sets = "";
			for (ExportedKey ek : eksList) {
				sets += "\n\t\t<set name=\"" + this.getAttributeName(ek.fktable_name) + "s\" inverse=\"true\">\n" + "\t\t\t<key column=\""
						+ ek.fkcolumn_name.toUpperCase() + "\"></key>\n" + "\t\t\t<one-to-many class=\"" + this.getPackage(FileType.MODEL) + "."
						+ this.getClassName(ek.fktable_name) + "\" />\n" + "\t\t</set>";
			}
			idContent += sets;
		}
		content = content.replace("{content}", idContent);
		return content;
	}
}
